﻿using Core.Framework;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;

namespace Core.DataAccess.Model
{
    public class iToolLoggerSaveProvider
    {
        private readonly IDBFactory iDBFactory;
        private readonly Subject<LogException> subject;
        private const string tabelName = "[LogSystemException]";

        public iToolLoggerSaveProvider(IDBFactory iDBFactory)
        {
            this.iDBFactory = iDBFactory;
            this.subject = new Subject<LogException>();
            this.subject
                .Buffer(TimeSpan.FromSeconds(5), 100)
                .Where(x => x.Count > 0)
                .Select(list => Observable.FromAsync(() => BatchInsertData(list)))
                .Concat()
                .Subscribe();

        }

        public void InsertItem(LogException log)
        {
            this.subject.OnNext(log);
        }

        private Task BatchInsertData(IList<LogException> list)
        {
            DataTable table = DataConver.ListToDataTable(tabelName, list);

            using (var connection = this.iDBFactory.Create(DatabaseType.Logger) as SqlConnection)
            {
                var bulkCopy = new SqlBulkCopy(connection);
                bulkCopy.DestinationTableName = tabelName;
                bulkCopy.BulkCopyTimeout = 0;
                bulkCopy.WriteToServer(table);
            }

            return Task.CompletedTask;
        }

    }

    public class iToolLogger : ILogger, IDisposable
    {

        public string categoryName;
        private readonly iToolLoggerSaveProvider saveProvider;
        private readonly string[] filterCategoryNames;
        private readonly string[] ignoreLogLevel = new string[] { "Trace", "Information" };

        public iToolLogger(iToolLoggerSaveProvider saveProvider)
        {
            this.saveProvider = saveProvider;
            string filterNameString = "ActivationDirectory,AdoNetGrainStorage,AdoNetClusteringTable,ClusterHealthMonitor,ClusterMembershipService,Dashboard,GrainTypeManager,ManagementGrain,MembershipAgent,MembershipTableCleanupAgent,MembershipTableManager,NoOpHostEnvironmentStatistics,PubSubRendezvousGrain,SiloLifecycleSubject,SiloOptionsLogger,SiloStatusListenerManager,MessageFactory,MembershipGossiper,";

            this.filterCategoryNames = filterNameString.Split(',');
        }

        public IDisposable BeginScope<TState>(TState state) => this;

        public void Dispose() { }

        public bool IsEnabled(LogLevel logLevel) => true;

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!this.filterCategoryNames.Contains(this.categoryName) || logLevel.ToString() == "Error")
            {
                if (ignoreLogLevel.Contains(logLevel.ToString()))
                {
                    return;
                }


                var model = new LogException
                {
                    level = logLevel.ToString(),
                    category = this.categoryName,
                    message = formatter(state, exception),
                    exception = exception?.TryToJson() ?? "",
                    createTime = DateTime.Now
                };
                this.saveProvider.InsertItem(model);
            }
        }

    }

    public class iToolLoggerCategory<T> : iToolLogger, ILogger<T>
    {
        public iToolLoggerCategory(iToolLoggerSaveProvider saveProvider)
            : base(saveProvider)
        {
            base.categoryName = typeof(T).Name;
        }
    }


}
